# app_manager.py
import os

from pywinauto import Application, Desktop
import time
import subprocess
import psutil
from typing import Optional

from config.data import WINDOW_CONFIG
from config.settings import APP_CONFIG
from tools.logger import log_info, log_debug, log_error, log_warning


class AppManager:
    """
    上位机应用管理器（支持 pywinauto）
    """

    def __init__(self, exe_path: str = None):
        self.app_path = exe_path or APP_CONFIG["exe_path"]
        self.app: Optional[Application] = None
        self.process: Optional[subprocess.Popen] = None
        self._main_window = None  # 主窗口实例（WindowSpecification）
        self._window_cache = {}  # 窗口缓存字典

    def start_app(self, timeout: int = 10):  # 默认超时10秒
        """
        启动上位机应用（带残留清理 + 显式等待）
        :param timeout: 等待主窗口出现的超时时间
        :return: Application 实例
        """
        log_info("🚀 开始启动上位机应用...")

        # 1. 启动前：确保没有残留进程
        self._ensure_clean_start()

        # 2. 启动应用
        try:
            log_info(f"🎮 正在启动: {self.app_path}")
            self.process = subprocess.Popen(
                self.app_path,
                stdout=subprocess.PIPE,
                stdin=subprocess.PIPE,
                stderr=subprocess.PIPE,
                encoding='utf-8',
                errors='ignore'
            )
            log_info(f"mPid:{self.process.pid}")
        except Exception as e:
            log_error(f"❌ 启动应用失败: {e}")
            raise

        # 使用 pywinauto 连接
        try:
            log_info("🔍 正在连接 pywinauto...")
            exe_name = os.path.basename(self.app_path)
            self.app = Application(backend=APP_CONFIG["backend"])
            self.app.connect(path=exe_name)

            # 获取主窗口配置（关键修复：使用包含匹配）
            main_cfg = WINDOW_CONFIG["main"]
            title_re = main_cfg.get("title_re", "条码关联系统")

            # 修复：使用包含匹配 (添加 .*)
            title_re = f"{title_re}.*"
            log_info(f"🔍 修正窗口匹配规则: {title_re}")

            # 打印当前所有窗口标题（用于调试）
            # self._debug_windows()

            self._main_window = self.app.window(title_re=title_re)
            log_info(f"⏳ 等待主窗口就绪: {title_re}")

            self._main_window.wait("visible", timeout=timeout, retry_interval=1)
            log_info(f"✅ 主窗口已就绪: {self._main_window.window_text()}")
            log_info("✅ 启动上位机成功")

            if not self._is_production_setting_visible():
                raise Exception("生产信息设置窗口未显示")

        except Exception as e:
            log_error(f"❌ 等待主窗口失败: {e}")
            # 添加调试信息
            self._debug_windows()
            raise

    def _is_production_setting_visible(self):
        """检查‘生产信息设置’窗口是否可见"""
        try:
            window_spec = self.app.window(title="生产信息设置")
            return window_spec.exists(timeout=5)
        except Exception as e:
            log_warning(f"⚠️ 检查'生产信息设置'窗口时遇到异常: {e}")
            return False

    @staticmethod
    def _debug_windows():
        """调试：打印所有当前窗口标题"""
        try:
            windows = Desktop(backend=APP_CONFIG["backend"]).windows(visible_only=True)
            log_debug("===== 当前所有可见窗口标题 =====")
            for win in windows:
                try:
                    title = win.window_text().strip()
                    auto_id = getattr(win, 'automation_id', lambda: None)
                    if title or auto_id:
                        log_debug(f"  - Title: '{title}' | AutoID: {auto_id}")
                except Exception:
                    # 忽略无法读取的窗口
                    pass
            log_debug("==========================")
        except Exception as e:
            log_error(f"⚠️ 窗口调试失败: {e}")

    @staticmethod
    def _ensure_clean_start():
        """启动前清理所有旧的 Test-System.exe 进程"""
        log_info("🧹 开始清理旧进程...")
        killed = 0
        try:
            for proc in psutil.process_iter(['pid', 'name', 'exe']):
                try:
                    if proc.info['exe'] and 'Test-System.exe' in str(proc.info['exe']):
                        log_info(f"💀 发现旧进程 PID={proc.info['pid']}，正在强制终止...")
                        proc.kill()
                        killed += 1
                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                    continue
            log_info(f"✅ 已清理 {killed} 个旧进程" if killed else "🟢 无旧进程需要清理")
        except Exception as e:
            log_warning(f"⚠️ 清理旧进程时发生异常: {e}")

    def close_app(self):
        """安全关闭应用：先尝试优雅关闭，再强制终止"""
        log_info("🛑 正在关闭应用...")
        log_info("🛑 开始关闭上位机应用...")

        # 1. 尝试通过 UI 优雅关闭
        try:
            if self._main_window and self._main_window.exists():
                log_info("🖱 尝试发送关闭消息...")
                self._main_window.close()
                time.sleep(0.2)
                if not self._main_window.exists():
                    log_info("✅ 应用已通过 UI 关闭")
                    self._cleanup()
                    return
        except Exception as e:
            log_info(f"⚠️ 优雅关闭失败: {e}")

        # 2. 尝试 terminate 进程
        if self.process:
            try:
                proc = psutil.Process(self.process.pid)
                if proc.is_running():
                    log_info(f"⚠️ 执行 terminate() 终止进程 {proc.pid}")
                    proc.terminate()
                    try:
                        proc.wait(timeout=5)
                        log_info(f"✅ 进程 {proc.pid} 已正常退出")
                        self._cleanup()
                        return
                    except psutil.TimeoutExpired:
                        log_info(f"⚠️ 进程 {proc.pid} terminate 超时，执行 kill()")
                else:
                    log_info("🟢 进程已不存在")
                    self._cleanup()
                    return
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                log_info("🟢 进程已被其他方式关闭")
                self._cleanup()
                return
            except Exception as e:
                log_error(f"❌ terminate 过程异常: {e}")

        # 3. 最终手段：taskkill 强杀（包括子进程）
        try:
            log_info("💥 执行 taskkill 强制结束进程树: Test-System.exe")
            result = subprocess.run(
                ['taskkill', '/f', '/t', '/im', 'Test-System.exe'],
                capture_output=True,
                text=True,
                encoding='gbk',
                errors='ignore'
            )
            if result.returncode == 0:
                log_info(f"✅ taskkill 成功: {result.stdout.strip()}")
            else:
                log_warning(f"⚠️ taskkill 非零退出码: {result.returncode}, 输出: {result.stdout}")
        except Exception as e:
            log_error(f"❌ taskkill 执行异常: {e}")

        self._cleanup()

    def _cleanup(self):
        """统一清理内部状态"""
        self.app = None
        self.process = None
        self._main_window = None
        self._window_cache = {}
        log_info("🧹 AppManager 状态已清理")

    def is_running(self) -> bool:
        """检查应用是否在运行"""
        if self.process and self.process.poll() is None:
            return True
        if self.process:
            try:
                proc = psutil.Process(self.process.pid)
                return proc.is_running()
            except psutil.NoSuchProcess:
                return False
        return False

    def restart_app(self):
        """重启应用"""
        log_info("🔄 正在重启应用...")
        self.close_app()
        time.sleep(0.5)
        self.start_app()
        log_info("✅ 应用已重启成功")

    def connect_window(self, key: str, timeout: int = 5, retry_interval: float = 1):
        config = WINDOW_CONFIG.get(key)
        if not config:
            raise KeyError(f"未找到窗口配置: {key}，可用: {list(WINDOW_CONFIG.keys())}")

        # 过滤无效的配置项
        valid_keys = {"title","title_re","class_name","process","visible_only","found_index"}
        filtered_config = {k: v for k, v in config.items() if k in valid_keys}

        log_info(f"🔍 正在连接窗口: {filtered_config.get('title', filtered_config.get('title_re', 'Unknown'))} ...")

        is_popup = config.get("is_popup",False)
        if is_popup:
            desktop = Desktop(backend=APP_CONFIG["backend"])
            window_spec = desktop.window(**filtered_config)
        else:
            window_spec = self.app.window(**filtered_config)

        window_spec.wait('visible', timeout=timeout, retry_interval=retry_interval)
        window_spec.set_focus()

        self._window_cache[key] = window_spec

        window_title = window_spec.window_text()
        log_info(f"✅ 成功连接到窗口: {window_title} (Key: {key})")
        return window_spec

    # === 快捷连接方法 ===
    def connect_to_login(self):
        return self.connect_window("login")

    def connect_to_main(self):
        return self.connect_window("main")

    def connect_to_domain_account(self):
        return self.connect_window("domain_account")

    def connect_to_relation_inquiry(self):
        return self.connect_window("relation_inquiry")

    def connect_to_key_confirm_form(self):
        return self.connect_window("key_confirm_form")

    def get_app(self):
        return self.app


# === 兼容旧接口 ===
def start_app():
    app_manager = AppManager()
    app_manager.start_app()
    log_info("🚀 启动成功")
    return app_manager


def debug_children(window, depth=0, prefix=""):
    indent = "  " * depth
    try:
        name = window.window_text()
        auto_id = getattr(window, 'automation_id', lambda: None)()
        class_name = getattr(window, 'class_name', lambda: None)()
        control_type = getattr(window, 'control_type', 'Unknown')
        log_debug(f"{indent}{prefix} 🪟 '{name}' | AutoID: {auto_id} | Class: {class_name} | Type: {control_type}")
    except Exception as e:
        log_error(f"{indent}{prefix} 🚫 [无法获取信息]：{e}")

    try:
        children = window.children()
        for i, child in enumerate(children):
            debug_children(child, depth + 1, f"C{i}")
    except Exception as e:
        log_error(f"{indent} 🚫 [无法获取子元素]: {e}")